/* MIXER.C
 * Sound blaster mixer routines
 *************
 * 29.08.2000 : Toni R„s„nen
 *              modifications/documentation for open source
 *
 */

// sample load-function: malloc, lock, store
//        pseudo: samload (char *dataisinherenow, char *allocsample)
// sample free function: unlock, free
//        pseudo: samfree (char *releaseme)

// get old int
// lock all regions for handler:  __dpmi_lock_linear_region
// set new int

// use only __go32_dpmi_xxxxxx -functions!
// set '_CRT0_FLAG_LOCK_MEMORY' bit in the `_crt0_startup_flags

#include <go32.h>
#include <stdio.h>
#include <conio.h>
#include <dpmi.h>
#include "mixer.h"
//#include "gus.h"
#include <sys/movedata.h>

int mixer_active = 0;

volatile int snd_mixfreq;

volatile int nomixcount;

volatile char snd_mask,snd_himask;
int disp_audioinfo;

// lock these if they are in use...
// for now, they aren't.
volatile int bufadx;
volatile char *buffer;
int sends;
int disable_16bitmix;

char dis_autoinit;

typedef 	void 	(*PFV)();

// LOCK-MUST DATA BEGIN HERE-------------
char lock_data_area_begin;
volatile int mix_stereo; // 0=mono, 1=stereo
char mix16bit = 0; // mix 16bit; only for sb16+ (dsp 4.00+)
int tobegadx;
volatile char snd_device = 0; // 0=none, 1=gus, 2=sb (mono/stereo)
volatile char mix_channels; // channels to mix
int sndio,snddma,sndirq,sndhighdma,sndhdmamask;
volatile mixerPFV mix_call_routine;
volatile int mix_tempbuff[4096];
int dmasegbase;
int dmadifference;
int dspver;
char autoinit;
volatile char mix_balance[32];    // balance; 0-15
volatile int mix_speed[32];       // play speed, in 16.16fixed; 0=don't play
char *mix_sample[32];    // sample adxs
volatile unsigned int mix_adx[32];         // current offset
volatile unsigned int mix_end[32];         // sample end adx
volatile unsigned int mix_loop[32];         // sample loop adx
volatile int mix_volume[32];      // mixing volume
volatile PFV sndendfunction[32];
volatile int intfromchannel;

volatile int realmixbuf,mixbufflen;
volatile int mix_play = 0;
volatile int dmaseg,dmaselector; // DMA buffer segment & selector (use selector to all!)
                        // note: dmaseg actually is PHYSICAL ADDRESS to data
volatile unsigned char dma_adxLO[2],dma_adxHI[2],dma_adxPAGE[2];
char lock_data_area_end;

int unsigned16bit; // sblive fix; change to unsigned

volatile int volreducetable[88*16];

volatile short interptable[8][512];
int use_interpolation = 1;
int force_mixfreq;

/*int i,toadx;
int bal,chn,volL,volR;
unsigned int spd,ofs,end;
char *sample;
int t;*/

// LOCK-MUST DATA END HERE-------------------------
// first:mix_Stereo
// last: dma_adxPAGE
// size: dma_adxPAGE+2 ...?


_go32_dpmi_seginfo SB_oldint,sbinfo;
int sb_int;
_go32_dpmi_seginfo dmainfo;

void calc_dmaadx()
{
   if (mix16bit)
     {  dma_adxPAGE[0] = (dmaseg>>17) & 0xff;
        dma_adxHI[0] = (dmaseg>>9) & 0xff;
        dma_adxLO[0] = (dmaseg>>1) & 0xff;
        dmaseg += (2048);

        dma_adxPAGE[1] = (dmaseg>>17) & 0xff;
        dma_adxHI[1] = (dmaseg>>9) & 0xff;
        dma_adxLO[1] = (dmaseg>>1) & 0xff;
        dmaseg -= (2048);
     }
   else
     {  dma_adxPAGE[0] = (dmaseg>>16) & 0xff;
        dma_adxHI[0] = (dmaseg>>8) & 0xff;
        dma_adxLO[0] = (dmaseg) & 0xff;
        dmaseg += (2048);

        dma_adxPAGE[1] = (dmaseg>>16) & 0xff;
        dma_adxHI[1] = (dmaseg>>8) & 0xff;
        dma_adxLO[1] = (dmaseg) & 0xff;
        dmaseg -= (2048);
     }
}


void alloc_dmabuffer(int dmasize)
{
   dmainfo.size = (dmasize+15)>>4;
   _go32_dpmi_allocate_dos_memory (&dmainfo);
   dmaseg = (dmainfo.rm_segment) << 4;
   dmaselector = dmainfo.pm_selector;

   dmasegbase = 0;
   if ( ((dmaseg+2048) >> 16) != (dmaseg >> 16) )
     {  dmaseg += 2048; dmasegbase = 2048;  }
   tobegadx = dmasegbase;

   calc_dmaadx();
//   dmaseg = (__dpmi_allocate_dos_memory ( (dmasize+15)>>4, &dmaselector ) ) << 4;
}


void free_dmabuffer()
{
   _go32_dpmi_free_dos_memory (&dmainfo);
//   __dpmi_free_dos_memory (dmaselector);
}

void autoinit_program_dmac(int size)
{
   __asm__ ("cli");
   if (mix16bit)
     {  // prime DMAC - 16bit dma channels
        
        outportb (0xD4, 0x04+sndhdmamask );
        outportb (0xD8, 0x00);
        outportb (0xD6, 0x58+sndhdmamask );
        outportb (0xC0+(sndhdmamask*4), dma_adxLO[0]);
        outportb (0xC0+(sndhdmamask*4), dma_adxHI[0]);
        switch (sndhighdma)
          {  case 5 : { outportb (0x8B, dma_adxPAGE[0]); break; }
             case 6 : { outportb (0x89, dma_adxPAGE[0]); break; }
             case 7 : { outportb (0x8A, dma_adxPAGE[0]); break; }
          }
        outportb (0xC2+(sndhdmamask*4), (size & 0xff) );
        outportb (0xC2+(sndhdmamask*4), ((size >> 8) & 0xff) );
        outportb (0xD4, sndhdmamask);
     }
   else
     {
        // prime DMAC - 8bit dma channels

        outportb (0x0A, 0x04+snddma);
        outportb (0x0C, 0x00);
        outportb (0x0B, 0x58+snddma); // memory to IO transfer - AUTOINIT MODE
        outportb (snddma*2, dma_adxLO[0]);
        outportb (snddma*2, dma_adxHI[0]);
        switch (snddma)
          {  case 0 : { outportb (0x87, dma_adxPAGE[0]); break; }
             case 1 : { outportb (0x83, dma_adxPAGE[0]); break; }
             case 3 : { outportb (0x82, dma_adxPAGE[0]); break; }
          } //switch snddma
        outportb (0x01+(snddma*2), (size & 0xff) );
        outportb (0x01+(snddma*2), ((size>>8) & 0xff) );
        outportb (0x0A, snddma);
     }
   __asm__ ("sti");
}

// LOCK THESE !!! ---------------------------------------------------
//   ...from here down

static volatile void mixercode_begin(){}

//***********************************************
// external definitions - in mixera.asm
extern void stereo_mixer_dma_store_8bit(); // basic models
extern void stereo_mixer_dma_store_16bit(); // sb pro/pro2/live
extern void stereo_mixer_dma_store_16bit_unsigned(); // for sb live
extern void mono_mixer_dma_store_8bit();
extern void mono_mixer_dma_store_16bit();

//******************************************

void waitsb()
{  int i;
   for (i=0;i<0x25;i++)
     inportb (sndio+0x0E);
}

void sbwaitfordata()
{  int delay = 300000;
   while ( ( (inportb (sndio+0x0E) & 0x80) == 0 ) && (delay>0) )
     { delay--; }
}

void startdma (int size)
{
/*   unsigned char control;
   if (snd_device == 1)
     {    // DEVICE1=GUS
          // gus can be prepared for DMA before DMAC
          control = 0x80; // // signed, irq; bit2=16bit chan | STOP!
          outportb (gus_select, SPC_DMA_CTRL);
          outportb (gus_hidata, control);

          outportb (gus_select, SPC_DMA_START_ADX);
          outportw (gus_lodata, 2+((mix_play*realmixbuf)>>4) );
             // specify DRAM adx (DRAM #32+(play*realmixbuf) )

          outportb (gus_select, SPC_DMA_CTRL);
          outportb (gus_hidata, control | 0x01); // GO ON WITH TRANSFER!!!

      } */

   if (mix16bit)
     {  // prime DMAC - 16bit dma channels
        
        outportb (0xD4, 0x04+sndhdmamask );
        outportb (0xD8, 0x00);
        outportb (0xD6, 0x48+sndhdmamask );
        outportb (0xC0+(sndhdmamask*4), dma_adxLO[mix_play]);
        outportb (0xC0+(sndhdmamask*4), dma_adxHI[mix_play]);
        switch (sndhighdma)
          {  case 5 : { outportb (0x8B, dma_adxPAGE[mix_play]); break; }
             case 6 : { outportb (0x89, dma_adxPAGE[mix_play]); break; }
             case 7 : { outportb (0x8A, dma_adxPAGE[mix_play]); break; }
          }
        outportb (0xC2+(sndhdmamask*4), (size & 0xff) );
        outportb (0xC2+(sndhdmamask*4), ((size >> 8) & 0xff) );
        outportb (0xD4, sndhdmamask);
     }
   else
     {  // prime DMAC - supports only LOW dma-channels (at least, for now)
        outportb (0x0A, 0x04+snddma);
        outportb (0x0C, 0x00);
        outportb (0x0B, 0x48+snddma ); // memory to IO transfer
        outportb (snddma*2, dma_adxLO[mix_play]);
        outportb (snddma*2, dma_adxHI[mix_play]);
        switch (snddma)
          {  case 0 : { outportb (0x87, dma_adxPAGE[mix_play]); break; }
             case 1 : { outportb (0x83, dma_adxPAGE[mix_play]); break; }
             case 3 : { outportb (0x82, dma_adxPAGE[mix_play]); break; }
          } //switch snddma
        outportb (0x01+(snddma*2), (size & 0xff) );
        outportb (0x01+(snddma*2), ((size>>8) & 0xff) );
        outportb (0x0A, snddma);
          // ...and the transfer is on by now (with GUS, that is)
     }

   if ( (snd_device == 2) )
     { //DEVICE2=SB
       // new blasters use autoinit mode, this function is not used..
       outportb (sndio+0x0C, 0x14);
       size--;
       waitsb();
       outportb (sndio+0x0C, (size & 0xff) );
       waitsb();
       outportb (sndio+0x0C, ((size>>8) & 0xff) );
     } //switch

}

void mix_next_area_STEREO ()
{
   int i;
   int chn,volL,volR;
   unsigned int spd,end;
   int t,td;

   int interpos;

   int toadx;
   char* sample;
   int bal;
   unsigned int ofs;

   for (i=0;i<=realmixbuf;i++)
     mix_tempbuff[i] = 0;

   for (chn=0;chn<mix_channels;chn++) // channel mix loop
     if (spd=mix_speed[chn])
       {  volL = volreducetable[ mix_volume[chn]*mix_balance[chn] ];
          volR = volreducetable[ mix_volume[chn]*(15-mix_balance[chn]) ];
          ofs = mix_adx[chn];
          end = mix_end[chn];
          sample = mix_sample[chn];
          toadx = 0;
          bal = mix_balance[chn];
          for (i=0;i<=mixbufflen;i++)
            {  t = *(sample+(ofs>>15) );

               if (use_interpolation)
                 {  td = ( *(sample+(ofs>>15)+1 ) - t);
                    interpos = (ofs >> 12) & 7;
                    t = t + interptable[interpos][td+256];
                 }

               mix_tempbuff[toadx++] += ( t*(volL) );
               mix_tempbuff[toadx++] += ( t*(volR) );
               
               ofs += spd;
               if ( ofs >= end )
                 {  if (sndendfunction[chn] == NULL)
                      {
                         if (mix_loop[chn] != 1)
                           {  ofs = mix_loop[chn];  }
                         else
                           {  mix_speed[chn] = 0; break; }
                      }
                    else
                      {  intfromchannel = chn;
                         sndendfunction[chn]();

                         ofs = mix_adx[chn];
                         end = mix_end[chn];
                         sample = mix_sample[chn];
                         volR = volreducetable[mix_volume[chn]*mix_balance[chn]];
                         volL = volreducetable[mix_volume[chn]*(15-mix_balance[chn])];
                      }
                 }
            }
          mix_adx[chn] = ofs;
       }

   if (mix16bit)
     {
       if (unsigned16bit)
         stereo_mixer_dma_store_16bit_unsigned();
       else
         stereo_mixer_dma_store_16bit();
/*       _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i])>>3 );
             if (t < -32768) t = -32768;
             if (t > 32767) t = 32767;
             t ^= 0x8000;
             _farnspokew (toadx++, t );
             toadx++;
//             _farnspokeb (toadx++, (t >> 8) );
          } */
     }
   else
     {
        stereo_mixer_dma_store_8bit();

/*        _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i]>>10) );
             if (t < -128) t = -128;
             if (t > 127) t = 127;
             _farnspokeb (toadx++, (t+128) );
//             _farnspokeb (toadx++, (0) );

//             if (bufadx)
//             *(buffer+bufadx++) = _farnspeekb (toadx-1);
          } */


     }

   sends++;

}

void mix_next_area_MONO ()
{
   int i,toadx;
   int bal,chn,vol;
   unsigned int spd,ofs,end;
   char *sample;
   int t;

   for (i=0;i<=realmixbuf;i++)
     mix_tempbuff[i] = 0;

   for (chn=0;chn<mix_channels;chn++) // channel mix loop
     if (spd=mix_speed[chn])
       {
          ofs = mix_adx[chn];
          end = mix_end[chn];
          sample = mix_sample[chn];
          vol = volreducetable[ mix_volume[chn]<< 4 ] >> 4;
          toadx = 0;
          bal = mix_balance[chn];
          for (i=0;i<mixbufflen;i++)
            {  t = *(sample+(ofs>>15) );
               t *= vol;
               mix_tempbuff[toadx++] += t;
               ofs += spd;
               if ( ofs >= end )
                 {  if (sndendfunction[chn] == NULL)
                      {  if (mix_loop[chn] != 1)
                           {  ofs = mix_loop[chn];  }
                         else
                           {  mix_speed[chn] = 0; break; }
                      }
                    else
                      {  intfromchannel = chn;
                         sndendfunction[chn]();

                         ofs = mix_adx[chn];
                         end = mix_end[chn];
                         sample = mix_sample[chn];
                         vol = volreducetable[ mix_volume[chn]<< 4 ] >> 4;
                      }
                 }
            }
          mix_adx[chn] = ofs;
       }

/*   if (mix16bit)
     {  _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i])<<1 );
             if (t < -32768) t = -32768;
             if (t > 32767) t = 32767;
             t ^= 0x8000;
             _farnspokew (toadx++, t );
             toadx++;
//             _farnspokeb (toadx++, (t & 0xff) );
//             _farnspokeb (toadx++, (t >> 8) );
          }
     }
   else
     {
        _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i]>>6) );
             if (t < -128) t = -128;
             if (t > 127) t = 127;
             _farnspokeb (toadx++, (t+128) );

//             if (bufadx)
//               *(buffer+bufadx++) = _farnspeekb (toadx-1);
//             _farnspokeb (toadx++, (0) );
          }
     }*/

   if (mix16bit)
     {
/*        if (unsigned16bit)
          mono_mixer_dma_store_16bit_unsigned();
        else */
          mono_mixer_dma_store_8bit();
/*       _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i])>>3 );
             if (t < -32768) t = -32768;
             if (t > 32767) t = 32767;
             t ^= 0x8000;
             _farnspokew (toadx++, t );
             toadx++;
//             _farnspokeb (toadx++, (t >> 8) );
          } */
     }
   else
     {
        mono_mixer_dma_store_8bit();
        /*
        _farsetsel (dmaselector);
//        toadx = ( mix_play*dmadifference + dmasegbase );
        toadx = tobegadx;
        for (i=0;i<=realmixbuf;i++)
          {  t = ( (mix_tempbuff[i]>>10) );
             if (t < -128) t = -128;
             if (t > 127) t = 127;
             _farnspokeb (toadx++, (t+128) );
//             _farnspokeb (toadx++, (0) );

//             if (bufadx)
//             *(buffer+bufadx++) = _farnspeekb (toadx-1);
          }
        */

     }

   sends++;
}

void mix_next_area ()
{  if (mix_stereo)
     {  mix_next_area_STEREO (); }
   else
     {  mix_next_area_MONO (); }
}

// also the interrupt service routine!
void sendarea()
{  int i;
   __asm__ ("cli");
   nomixcount = 0;
   switch (snd_device)
     { /*case 1: {  //GRAVIS
                  if (mix_stereo)
                      {  gus_playvoice (0,32,0,30+realmixbuf,0x20,44100);
                         gus_playvoice (1,33,0,31+realmixbuf,0x00,44100);
                      }
                    else
                      {  gus_playvoice (0,32,0,31+realmixbuf,0x20,22050);
                      } */


                    // loop is preprogrammed - only endadxs & loop bit
                    // need to be set

/*                  startdma (realmixbuf);
                  if (mix_play == 0)
                    { // voice in middle - program END->BUFFEND & LOOP
                       i = 30+realmixbuf*2-1;
                       outportb (gus_baseadx+VOICE_REG, 0);
                       outportb (gus_select, VCE_END_ADX_HI);
                       outportw (gus_lodata, gus_adx_high(i));
                       outportb (gus_select, VCE_END_ADX_LO);
                       outportw (gus_lodata, gus_adx_low(i));
                       outportb (gus_select, VCE_CTRL);
                       outportb (gus_hidata, 0x28);

                       i++;
                       outportb (gus_baseadx+VOICE_REG, 1);
                       outportb (gus_select, VCE_END_ADX_HI);
                       outportw (gus_lodata, gus_adx_high(i));
                       outportb (gus_select, VCE_END_ADX_LO);
                       outportw (gus_lodata, gus_adx_low(i));
                       outportb (gus_select, VCE_CTRL);
                       outportb (gus_hidata, 0x08);
                    }
                  else
                    { // voice in end - program END->BUFFMIDDLE & NO LOOP
                       i = 30+realmixbuf-1;
                       outportb (gus_baseadx+VOICE_REG, 0);
                       outportb (gus_select, VCE_END_ADX_HI);
                       outportw (gus_lodata, gus_adx_high(i));
                       outportb (gus_select, VCE_END_ADX_LO);
                       outportw (gus_lodata, gus_adx_low(i));
                       outportb (gus_select, VCE_CTRL);
                       outportb (gus_hidata, 0x24);

                       i++;
                       outportb (gus_baseadx+VOICE_REG, 1);
                       outportb (gus_select, VCE_END_ADX_HI);
                       outportw (gus_lodata, gus_adx_high(i));
                       outportb (gus_select, VCE_END_ADX_LO);
                       outportw (gus_lodata, gus_adx_low(i));
                       outportb (gus_select, VCE_CTRL);
                       outportb (gus_hidata, 0x04);
                    }


                  break;
               } //gus */
       case 2: {   // blaster
                  if (mix16bit)
                    inportb (sndio+0x0F); // whythefuck 16bit ACK differs...
                  else
                    inportb (sndio+0x0E); // ...from the 8bit ACK??? STUPID!

                  if (autoinit == 0) // stereo: autoinit, no dma start
                    {  startdma ( realmixbuf ); }
                  break;
               }
     }

   if (mix_call_routine != NULL)
     { mix_call_routine();  }

   mix_next_area();
   mix_play ^= 1;

   if (!mix_play)
     tobegadx += (dmadifference);
   else
     tobegadx -= (dmadifference);

   if (snd_device == 2)
     {  outportb (0x20, 0x20);
        if (sndirq > 7)
          outportb (0xA0, 0x20); // high-irq ack
     }
   __asm__ ("sti");
}

static volatile void mixercode_end(){}

//      from here up...
// LOCK THESE !!! ---------------------------------------------------

void sb_setdigispeed(int spd)
{
   waitsb();

     // command to directly set frequency; sb16+
   if ( (dspver > 0x400) )
     {  // no need to care 'bout stereo - this is independent of it
        outportb (sndio+0x0C, 0x41); waitsb();
        outportb (sndio+0x0C, (spd >> 8) ); waitsb();
        outportb (sndio+0x0C, (spd & 0xFF) ); waitsb();
     }
   else
     {  outportb (sndio+0x0C, 0x40);
        waitsb();
        if (mix_stereo)
          {  outportb (sndio+0x0C, 256-(500000 / spd) ); }
        else
          {  outportb (sndio+0x0C, 256-(1000000 / spd) ); }
     }
}

void close_mixer()
{  int i;
   if (snd_device == 0) return;

   free_dmabuffer();

   switch (snd_device)
     { /* case 1 : {   //1:gus
                    gus_close ();
                    break;
                 }   //1:gus */
        case 2 : {   //2:blaster
                    if (mix_stereo)
                      {  outportb (sndio+0x0C, 0xD0);
                         outportb (sndio+0x0C, 0xDA);
                         outportb (sndio+0x0C, 0xD0);
                      }

                    outportb (0x21,snd_mask); // restore mask
                    outportb (0xA1,snd_himask);

                    _go32_dpmi_set_protected_mode_interrupt_vector(sb_int, &SB_oldint);

                    outportb (sndio+6,1);
                    for (i=0;i<10;i++) waitsb();
                    outportb (sndio+6,0);
                    for (i=0;i<10;i++) waitsb();
                    break;
                 }   //2:blaster
     } //switch
   snd_device = 0;
}


void calc_interpolation()
{  int d,i;
   for (d = -256; d < 255; d++)
     {  for (i = 0; i < 8; i++)
          {  interptable[i][d+256] = d*i / 8;
          }
     }
}

void calc_volumetable (int level)
{  int i;
   for (i = 0; i < 88*16; i++)
     volreducetable[i] = ((i*level) / 256);
}

_go32_dpmi_seginfo soundtimer_oldint,soundtiminfo;
volatile long long soundtimer_originalcall;
int soundtimer_active = 0;

static void sound_timerint_function()
{
   nomixcount++;
   if (nomixcount > 20)
     { nomixcount = 0; reset_sound(); }

   __asm__ __volatile ("pushfl
                             lcall %0"
                            :
                            : "g" (soundtimer_originalcall) );
}

void set_soundtimer()
{
   if (soundtimer_active) return;
   _go32_dpmi_get_protected_mode_interrupt_vector(0x08, &soundtimer_oldint);
   soundtimer_originalcall = ((unsigned long long)soundtimer_oldint.pm_offset) +
                        (((unsigned long long)soundtimer_oldint.pm_selector)<<32);

   soundtiminfo.pm_offset=(unsigned long int)sound_timerint_function;
   soundtiminfo.pm_selector=_my_cs();
   _go32_dpmi_allocate_iret_wrapper(&soundtiminfo);
   _go32_dpmi_set_protected_mode_interrupt_vector(0x08, &soundtiminfo);
   soundtimer_active = 1;
}

void close_soundtimers()
{  if (!soundtimer_active) return;
   _go32_dpmi_set_protected_mode_interrupt_vector(0x08, &soundtimer_oldint);
}


void init_mixer(int dev, int io, int irq, int dma, int hdma, int channels )
{  int i,len;

    // if mixer active, disable old config
   if (snd_device) { close_mixer(); }

   if ( (dev == 0) )
     {  if (disp_audioinfo)
           printf ("No sound device\n");
        return;
     }

//   for (i = 0; i < 32; i++) mix_speed[i] = 0; }
   mix_play = 0;

   if (dev == 2) { mix_stereo = 0; } else { mix_stereo = 1; }
   if (dev == 3) { dev--; }

   if (disp_audioinfo)
     {
        printf ("Sound device in use: ");
        if (dev == 1)
          printf ("Gravis UltraSound\n");
        else
          printf ("Sound Blaster\n");

        if (mix_stereo)
          printf ("Stereo\n");
        else
          printf ("Mono\n");
     }

   tobegadx = 0;

   snd_device = dev;
   sndio = io;
   sndirq = irq;
   snddma = dma;
   sndhighdma = hdma;
   sndhdmamask = hdma-4;
   mix_channels = channels;
   mix_call_routine = NULL;

   calc_volumetable (196);
   calc_interpolation();


   dmadifference = 2048;

   switch (snd_device)
     {  /*case 1 : {
                    gus_reset (28, sndio, sndirq,sndirq,snddma,snddma);
                    gus_enable_output();
                    gus_setvolume (0,64); gus_setvolume (1,64);

                    mixbufflen = 441;
                    if (mix_stereo)
                      {  realmixbuf = mixbufflen*2;  }
                    else
                      {  realmixbuf = mixbufflen;  }

                    alloc_dmabuffer(32768);

                    if (mix_stereo == 0)
                      {  gus_setbalance (0, 7);  }
                    else
                      {  gus_setbalance (0,0); gus_setbalance (1,0x0f);  }
                    girq_wave = sendarea;

                    mix_next_area();

                    autoinit = 1;
                    startdma (realmixbuf);
                      // 2c:irq,rollover, 0c:rollover
                    gus_playvoice (0,32,32,30+realmixbuf,0x24,44100);
                    gus_playvoice (1,33,33,31+realmixbuf,0x04,44100);

                    snd_mixfreq = 22050;

                    break;
                 } // case1:gus */
        case 2 : {
                    outportb (sndio+6,1);
                    for (i=0;i<10;i++) waitsb();
                    outportb (sndio+6,0);
                    for (i=0;i<10;i++) waitsb();

                    if (inportb (sndio+0x0A) != 0xAA)
                      {  snd_device = 0; return; }

                      // get dsp ver
                    outportb (sndio+0x0C, 0xE1); waitsb();
                    sbwaitfordata();
                    dspver = inportb (sndio+0x0A) << 8; waitsb();
                    sbwaitfordata();
                    dspver += inportb (sndio+0x0A); waitsb();

                      // if new enough DSP, use autoinit mode
                    if (dspver >= 0x200) autoinit = 2; else autoinit = 0;

                    if ( (dspver >= 0x400) )
                      {  mix16bit = 1; }
                    else
                      {  mix16bit = 0; }

                    snd_mixfreq = 22050;

                    if (dspver < 0x300) // basic sb
                      snd_mixfreq = 22050;
                    if ( (dspver >= 0x300) && (dspver < 0x400) )// sbpro
                      { if (mix_stereo)
                           snd_mixfreq = 22050;
                         else
                           snd_mixfreq = 44100;

                      }
                    if (dspver >= 0x400)
                      snd_mixfreq = 44100;

                    if (force_mixfreq > 4000)
                      if (snd_mixfreq >= force_mixfreq)
                        snd_mixfreq = force_mixfreq;

//                    autoinit = 0;
//                    mix16bit = 0;

                    mixbufflen = 441*snd_mixfreq / 22050;
                    if (mix_stereo)
                      {  realmixbuf = mixbufflen*2;  }
                    else
                      {  realmixbuf = mixbufflen;  }  

                    if (autoinit == 0)
                      mix16bit = 0;

                    if (disable_16bitmix == 1)
                      mix16bit = 0;

                    alloc_dmabuffer(32768);

                    if (disp_audioinfo)
                      {  printf ("Mixing at %i Hz; realmixbuf= %i bytes\n",snd_mixfreq,realmixbuf);
                         printf ("DMA Base : %6x\n", dmaseg);
                         printf ("Using IO %i, I %i, D %i, HD %i\n",sndio, sndirq, snddma, sndhighdma);
                         printf ("sb_init: DSP version %d.%2d\n",dspver>>8, dspver & 0xff);
                         if (use_interpolation)
                           printf ("Interpolation enabled\n");
                         if (unsigned16bit)
                           printf ("Using SB LIVE! correction\n");
                         if (autoinit)
                           printf ("Autoinitialized mode\n");
                         else
                           printf ("Single-cycle mode\n");

                         if (mix16bit)
                           printf ("16-bit output\n");
                         else
                           printf ("8-bit output\n");
                      }

                    calc_dmaadx();

                    outportb (sndio+0x0C, 0xD1); // turn on speaker
                    waitsb();
                    
                    snd_mask = inportb (0x21);
                    snd_himask = inportb (0xA1);
                    if (sndirq > 7)
                      { outportb (0xA1, snd_himask & (~(1<<(sndirq-8))) ); //enable int
                      }
                    else
                      { outportb (0x21, snd_mask & (~(1<<sndirq)) ); //enable int
                      }
                      
                    if (mix16bit)
                      inportb (sndio+0x0F); // pendings
                    else
                      inportb (sndio+0x0E); // pendings
                    waitsb();

                    sb_setdigispeed (snd_mixfreq); //mixing speed

                    // 16bit handling in autoinit only - 16bit is available
                    // from dsp4, autoinit from dps2, so no need to worry
                    // non-autoinitialized 16bit transfer - no such thing!

                    // clear mixing buffer
                    _farsetsel (dmaselector);
                    for (i=0;i<=realmixbuf*2; )
                      {  _farnspokeb (i++, 0 );  }

                    mix_next_area();

                    if (autoinit) //reset mixer to requireds
                      {
                         dmadifference = realmixbuf;

                         if (mix16bit)
                           {  dmadifference <<= 1;  }

                         tobegadx = dmadifference;

                         if (dspver < 0x400)
                           {    // not sb16 - old cmds
                              if (mix_stereo)
                                {  outportw (sndio+4, 0x0000); waitsb(); //reset mixer
                                   outportw (sndio+4, 0x220E); waitsb(); //mixer -> stereo
                                   outportw (sndio+4, 0xFF22); waitsb(); //master vol to max
                                   outportw (sndio+4, 0xFF04); waitsb(); //wave vol to max
                                }

                                // reset dsp to do AUTOINIT
                              autoinit_program_dmac(realmixbuf*2-1);

                              outportb (sndio+0x0C, 0x48); waitsb();
                              outportb (sndio+0x0C, (realmixbuf-1) & 0xFF); waitsb();
                              outportb (sndio+0x0C, (realmixbuf-1) >> 8); waitsb();

                              if (dspver == 0x200)
                                 {  outportb (sndio+0x0C, 0x1C); waitsb();  }
                              else
                                 {  outportb (sndio+0x0C, 0x90); waitsb();  }
                           } // if dsp < 4.00
                         else
                           {    // sb16 - use generic cmds
                                // cmd->mode->lenLO->lenHI
                              autoinit_program_dmac (realmixbuf*2-1);

                               // reset mixer
                /*              outportw (sndio+4, 0x0000); waitsb(); //reset mixer
                              outportw (sndio+4, 0xFF22); waitsb(); //master vol to max
                              outportw (sndio+4, 0xFF04); waitsb(); //wave vol to max
                */

                              if (mix16bit)
                                {  outportb (sndio+0x0C, 0xB6);  } // 16bit dma
                              else
                                {  outportb (sndio+0x0C, 0xC6);  } // 8bit dma

                              waitsb();
                              if (mix_stereo)
                                {  outportb (sndio+0x0C, 0x60); } // stereo, unsigned
                              else
                                {  outportb (sndio+0x0C, 0x40); } // mono, unsigned
                              waitsb();


                              outportb (sndio+0x0C, ( (realmixbuf-1) & 0xFF ) ); waitsb();
                              outportb (sndio+0x0C, ( (realmixbuf-1) >> 8 ) ); waitsb();
                           } // else

                      } // if autoinit

                      // set irq handler with the DPMI
                    if (sndirq > 7)
                      sb_int = sndirq+0x68; // int#
                    else
                      sb_int = sndirq+0x08; // int#
                    _go32_dpmi_get_protected_mode_interrupt_vector(sb_int, &SB_oldint);
                    sbinfo.pm_offset=(unsigned long int)sendarea;
                    sbinfo.pm_selector=_my_cs();
                    _go32_dpmi_allocate_iret_wrapper(&sbinfo);
                    _go32_dpmi_set_protected_mode_interrupt_vector(sb_int, &sbinfo);

                    sendarea();

                    break;
                 } // case2:sb
     } //switch


     // lock code and variables ...
     // pointer data should be locked _too_ but maybe it'd be too much...
     // mmmh... maybe a sample loader function to do that? yeah, that'd
     // work... maybe. do it!
   _go32_dpmi_lock_code(mixercode_begin,((char *)mixercode_end-(char *)mixercode_begin));
   _go32_dpmi_lock_data(&lock_data_area_begin,((char*)(&lock_data_area_end)-(char*)(&lock_data_area_begin)+1) );

   mixer_active = 1;
   set_soundtimer();
}


void mix_setfreq (int chn, int speed)
{  if (!mixer_active) return;
   mix_speed[chn] = 16384 * speed / (snd_mixfreq / 2); // stupid but works. blah.
}

void mix_setvolume (int chn, int vol)
{  if (!mixer_active) return;
   if (mix_stereo)
     mix_volume[chn] = vol * 11 / 8;
   else
     mix_volume[chn] = vol;
}

void mix_playsound (int chn, char *snd, int beg, int loop, int end, int speed)
{  if (!mixer_active) return;
   __asm__ ("cli;");
   mix_sample[chn] = snd;
   mix_adx[chn] = ((unsigned int)beg)<<15;
   mix_end[chn] = ((unsigned int)end)<<15;
   if (loop == -1)
     {  mix_loop[chn] = 1; }
   else
     {  mix_loop[chn] = ((unsigned int)loop)<<15; }
   mix_setfreq (chn, speed);
   __asm__ ("sti;");
}

void set_mixbuffsize ( int size )
{  int mb;
   if (snd_device == 1)
     {  mb = size & 0xfff0; }
   else
     {  mb = size & 0xfffc; }

   if (mb == mixbufflen)
     return; // same - no change

   mixbufflen = mb;
   if (mix_stereo)
     {  realmixbuf = mixbufflen*2;  }
   else
     {  realmixbuf = mixbufflen; }

   if (autoinit == 2) // SB ONLY! GUS unfortunately doesn't support autoinit this freely
     {  // AUTOINIT: set DMA block size
        if (dspver >= 0x400)
          {  // cmd->mode->lenLO->lenHI
//             outportb (sndio+0x0C, 0xD0);

             autoinit_program_dmac (realmixbuf*2-1);

             if (mix16bit)
               {  outportb (sndio+0x0C, 0xB6);  } // 16bit dma
             else
               {  outportb (sndio+0x0C, 0xC6);  } // 8bit dma

             waitsb(); // fifo 02h ???
             if (mix_stereo)
               {  outportb (sndio+0x0C, 0x40);  }
             else
               {  outportb (sndio+0x0C, 0x00);  }
             waitsb();
             outportb (sndio+0x0C, (realmixbuf-1) & 0xFF); waitsb();
             outportb (sndio+0x0C, (realmixbuf-1) >> 8); waitsb();
//             autoinit_program_dmac (realmixbuf*2-1);
          }
        else
          {  autoinit_program_dmac (realmixbuf*2-1);
             outportb (sndio+0x0C, 0x48); waitsb();
             outportb (sndio+0x0C, (realmixbuf-1) & 0xFF); waitsb();
             outportb (sndio+0x0C, (realmixbuf-1) >> 8); waitsb();
          }
        dmadifference = realmixbuf;
     }
}

void reset_sound()
{  int i;
   switch (snd_device)
     {  case 2 : // first, stop all prior activity
                 if (autoinit)
                   {   { outportb (sndio+0x0C,0xd3); // disable speaker
                         outportb (sndio+0x0C,0xd0); // halt dma
                         outportb (sndio+0x0C,0xda); // exit autoinit
                         outportb (sndio+0x0C,0xd0); // halt dma again
                       }
                   }

                 outportb (sndio+6,1); // reset blaster
                 for (i=0;i<50;i++) waitsb();
                 outportb (sndio+6,0);
                 for (i=0;i<50;i++) waitsb();
                 inportb (sndio+0x0A); // read ack - 0xAA

                 outportb(sndio+0x0C, 0xD1); // turn on speaker
                 waitsb();
                 snd_mask = inportb (0x21);
                 outportb (0x21, snd_mask & (!(1<<sndirq)) ); //enable int
                 if (mix16bit)
                   inportb (sndio+0x0F); // pendings
                 else
                   inportb (sndio+0x0E); // pendings
                 waitsb();

                 outportb (0x21, snd_mask & (~(1<<sndirq)) ); //enable int

                 sb_setdigispeed (snd_mixfreq); //mixing speed

                 if (autoinit)
                   if (dspver < 0x400)
                     {  if (mix_stereo)
                          { outportw (sndio+4, 0x0000); waitsb(); //reset mixer
                            outportw (sndio+4, 0x220E); waitsb(); //mixer -> stereo
                            outportw (sndio+4, 0xFF22); waitsb(); //master vol to max
                            outportw (sndio+4, 0xFF04); waitsb(); //wave vol to max
                          }

                         // reset dsp to do AUTOINIT
                         if (mix_stereo)
                           { autoinit_program_dmac(realmixbuf*2-1); }
                         else
                           { autoinit_program_dmac(realmixbuf*2); }

                         outportb (sndio+0x0C, 0x48); waitsb();
                         outportb (sndio+0x0C, (realmixbuf-2) & 0xFF); waitsb();
                         outportb (sndio+0x0C, (realmixbuf-2) >> 8); waitsb();

                         if (dspver == 0x200)
                           { outportb (sndio+0x0C, 0x1C); waitsb();  }
                         else
                           { outportb (sndio+0x0C, 0x90); waitsb();  }
                     }
                   else
                     { autoinit_program_dmac (realmixbuf*2-1);

                       if (mix16bit)
                         {  outportb (sndio+0x0C, 0xB6);  } // 16bit dma
                       else
                         {  outportb (sndio+0x0C, 0xC6);  } // 8bit dma

                       waitsb();
                       if (mix_stereo)
                         {  outportb (sndio+0x0C, 0x60); } // stereo, unsigned
                       else
                         {  outportb (sndio+0x0C, 0x40); } // mono, unsigned
                       waitsb();

                       outportb (sndio+0x0C, ( (realmixbuf-1) & 0xFF ) ); waitsb();
                       outportb (sndio+0x0C, ( (realmixbuf-1) >> 8 ) ); waitsb();
                     }

                 dmadifference = realmixbuf;

                 if (mix16bit)
                   { dmadifference <<= 1;  }

                 tobegadx = dmadifference;
                 mix_play = 0;
                 
                 mix_next_area();
                 sendarea();
                 break;
     }
}
/*
void init_sound()
{  FILE *tied = fopen ("sound.cfg","rb");
   short dev,io,dma,hdma,irq,sflip;
   int i;

   if (tied == NULL)
     {
        printf ("No audio information available. Running SETUP...\n");
        i = system ("SETUP.EXE");

        if (i == -1)
          printf ("Unable to run SETUP. Exiting ICE...\n");

        tied = fopen ("sound.cfg","rb");

        if (tied == NULL)
          {  printf ("Audio setup information still not available. Exiting ICE.\n");
             exit(0);
          }
     }

   fread ( &dev, 2,1, tied);
   fread ( &io, 2,1, tied);
   fread ( &irq, 2,1, tied);
   fread ( &dma, 2,1, tied);
   fread ( &hdma, 2,1, tied);
   fread ( &sflip, 2,1, tied);

   init_mixer ( dev, io, irq, dma, hdma, 18);

   fclose (tied);
}
*/
// *********************************************************************
void close_sound ()
{  mixer_active = 0;
   close_mixer();

}

